/***************************************************************************
 *   Copyright (C) 2008 by Silvan Imsand <imsandli@gmail.com>              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
 ***************************************************************************/

package com.sametime.twitterclient.views;

/*
 * TODO: 
 * - DONE: Refreshintervall is changed after restart and not directly
 * - DONE: ChangeListener for PreferencePage
 * - DONE: Multiline Table, or a nice window available by doubleclick
 * - Contextmenu for Website from Twitter friends
 * - Inform User about new messages
 * - DONE: Comments and cleanups
 * - the width of the second column after start is wrong (first twitter-refresh)
 */

import org.eclipse.jface.action.Action;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import com.sametime.twitterclient.Activator;
import com.sametime.twitterclient.preferences.PreferenceConstants;
import com.sametime.twitterclient.twitterapi.ITwitterListener;
import com.sametime.twitterclient.twitterapi.TwitterData;
import com.sametime.twitterclient.twitterapi.TwitterStatus;

/**
 * @since 1.0
 * @author Silvan Imsand
 */
public class TwitterView extends ViewPart implements ITwitterListener {
	static public TwitterView INSTANCE;

	private Composite twitterViewComp;

	private TableViewer tableViewer;

	private Table tableCtl;

	private static String columnHeaders[] = new String[PreferenceConstants.COLUMN_COUNT];

	private Text newTwitterText;

	private Label imageLabel;

	private Label twitterStatusLabel;

	private Button sendButton;

	private TableColumn tc1;

	private TableColumn tc2;

	private Action refreshAction;

	private Action sendMessageAction;

	private String errorMessage = new String("");

	/*
	 * The content provider class is responsible for providing objects to the
	 * view. It can wrap existing objects in adapters or simply return objects
	 * as-is. These objects may be sensitive to the current input of the view,
	 * or ignore it and always show the same content (like Task List, for
	 * example).
	 */

	/**
	 * draws the cell as three-liner and a picture in first column
	 *
	 */
	private class SameTwitTablePaintListener implements Listener {
		/* (non-Javadoc)
		 * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
		 */
		public void handleEvent(Event event) {
			if (event.type == SWT.PaintItem && event.item != null
					&& event.item.getData() != null
					&& event.index == PreferenceConstants.IMAGE_COLUMN) {

				// draw persona image scaled to standard size w/1 pixel border
				TwitterStatus status = ((TwitterStatus) event.item.getData());
				Rectangle r;
				Image pi = status.getImage();
				r = getDefaultUserImage().getBounds();
				Rectangle piRect = pi.getBounds();

				event.gc.drawImage(pi, 0, 0, piRect.width, piRect.height,
						event.x, event.y, r.width, r.height);
			}
			switch (event.type) {
			case SWT.MeasureItem: {
				TableItem item = (TableItem) event.item;
				String text = item.getText(event.index);
				Point size = event.gc.textExtent(text);
				event.width = size.x;
				event.height = Math.max(event.height, size.y);
				break;
			}
			case SWT.PaintItem: {
				TableItem item = (TableItem) event.item;
				String text = item.getText(event.index);
				Point size = event.gc.textExtent(text);
				int offset2 = event.index == 0 ? Math.max(0,
						(event.height - size.y) / 2) : 0;
				event.gc.drawText(text, event.x, event.y + offset2, true);
				break;
			}
			case SWT.EraseItem: {
				event.detail &= ~SWT.FOREGROUND;
				break;
			}
			}
		}
	}

	/**
	 * Contentprovider for the table
	 *
	 */
	private class SameTwitContentProvider implements IStructuredContentProvider {
		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
		 */
		public Object[] getElements(Object inputElement) {
			return ((TwitterData) inputElement).getRecentStatus().toArray();
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
		 */
		public void dispose() {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
		 */
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}
	}

	/**
	 * Maps a row with the TwitterStatus Object
	 *
	 */
	private class SameTwitLabelProvider implements ITableLabelProvider {
		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
		 */
		public Image getColumnImage(Object element, int columnIndex) {
			// see BuddiesTablePaintListener
			if (columnIndex == PreferenceConstants.IMAGE_COLUMN)
				return getDefaultUserImage();
			else
				return null;
		}

		public String getTooltipText(Object element) {
			TwitterStatus status = (TwitterStatus) element;
			return status.getMessage();
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
		 */
		public String getColumnText(Object element, int columnIndex) {
			TwitterStatus status = (TwitterStatus) element;

			if (columnIndex == PreferenceConstants.NAME_COLUMN) {
				return status.getScreenName() + "\n" + status.getMessage()
						+ "\n" + status.getMessageDate().toString();
			}
			return null;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
		 */
		public void addListener(ILabelProviderListener listener) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
		 */
		public void dispose() {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
		 */
		public boolean isLabelProperty(Object element, String property) {
			return true;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
		 */
		public void removeListener(ILabelProviderListener listener) {
		}
	}

	/**
	 * The constructor
	 */
	public TwitterView() {
		super();
		INSTANCE = this;
		TwitterData.addRefreshListener(this);
	}

//	public static TwitterView getInstance() {
//		return INSTANCE;
//	}

	/**
	 * This is a callback that will allow us to create the viewer and initialize
	 * it.
	 */
	public void createPartControl(Composite parent) {
		try {		
			twitterViewComp = parent;
			Composite comp = new Composite(parent, SWT.BORDER);
			comp.setLayout(new FormLayout());
			comp.setBackground(comp.getDisplay().getSystemColor(SWT.COLOR_WHITE));
	
			imageLabel = new Label(comp, SWT.IMAGE_PNG);
			imageLabel.setText("No image found");
			imageLabel.setSize(25, 25);
			FormData fd = new FormData();
			fd.top = new FormAttachment(0, 5);
			fd.left = new FormAttachment(0, 5);
			imageLabel.setLayoutData(fd);

			// get user Image
			imageLabel.setImage(getDefaultUserImage());

			sendButton = new Button(comp, SWT.PUSH);
			newTwitterText = new Text(comp, SWT.BORDER | SWT.SINGLE);
			twitterStatusLabel = new Label(comp, SWT.LEFT);

			// twitterStatusLabel Layout
			if (errorMessage != null) {
				if (!errorMessage.equals("")) {
					twitterStatusLabel.setText(errorMessage);
					twitterStatusLabel.setToolTipText(errorMessage);
					errorMessage = "";
				}
			}
			twitterStatusLabel.setBackground(comp.getDisplay().getSystemColor(
					SWT.COLOR_WHITE));
			fd = new FormData();
			fd.top = new FormAttachment(imageLabel, 0, SWT.TOP);
			fd.left = new FormAttachment(imageLabel, 5);
			fd.right = new FormAttachment(sendButton, 5);
			twitterStatusLabel.setLayoutData(fd);

			// newTwitterText Layout
			newTwitterText.setTextLimit(160); // max 160 chars
			fd = new FormData();
			fd.bottom = new FormAttachment(imageLabel, 0, SWT.BOTTOM);
			fd.left = new FormAttachment(imageLabel, 5);
			fd.right = new FormAttachment(sendButton, -5);
			newTwitterText.setLayoutData(fd);

			// sendButton Layout
			sendButton.setText("Send");
			fd = new FormData();
			fd.top = new FormAttachment(newTwitterText, 0, SWT.CENTER);
			fd.right = new FormAttachment(95);
			sendButton.setLayoutData(fd);

			// Dark line separator between business card and text area.
			Label divider = new Label(comp, SWT.NONE);
			divider.setBackground(divider.getDisplay().getSystemColor(
					SWT.COLOR_DARK_GRAY));
			fd = new FormData();
			fd.top = new FormAttachment(imageLabel, 5);
			fd.left = new FormAttachment(0, 0);
			fd.right = new FormAttachment(100, 0);
			fd.height = 1;
			divider.setLayoutData(fd);

			if (tableCtl != null)
				tableCtl.dispose();

			tableViewer = new TableViewer(comp, SWT.SINGLE | SWT.FULL_SELECTION
					| SWT.VERTICAL);
			tableCtl = tableViewer.getTable();

			// attach the table to the divider
			fd = new FormData();
			fd.top = new FormAttachment(divider, 5);
			fd.left = new FormAttachment(0, 0);
			fd.right = new FormAttachment(100, 0);
			fd.bottom = new FormAttachment(100, 0);
			tableCtl.setLayoutData(fd);
			
			// create columns
			createTableColumns();

			// align columns
			TableLayout layout = new TableLayout();
			layout.addColumnData(new ColumnPixelData(60, false, true));
			layout.addColumnData(new ColumnWeightData(100, 100, true));
			tableCtl.setLayout(layout);

			// Listeners
			addTableListeners();
			addTableControlListener(comp);
			addSendButtonListerner();
			
			// menu and actions
			makeActions();
			contributeToActionBars();			

			// set Data Input
			tableViewer.setInput(TwitterData.getInstance());

			parent.pack();
			parent.layout();
		} catch (Exception e) {
			// ignore
			e.printStackTrace();
		}

	}

	/**
	 * Automatically resize the table to window width
	 * @param comp
	 */
	private void addTableControlListener(final Composite comp) {
		comp.addControlListener(new ControlAdapter() {
			public void controlResized(ControlEvent e) {
				Rectangle area = comp.getClientArea();
				Point preferredSize = tableCtl.computeSize(SWT.DEFAULT,
						SWT.DEFAULT);
				int width = area.width - 2 * tableCtl.getBorderWidth();
				if (preferredSize.y > area.height + tableCtl.getHeaderHeight()) {
					// Subtract the scrollbar width from the total column width
					// if a vertical scrollbar will be required
					Point vBarSize = tableCtl.getVerticalBar().getSize();
					width -= vBarSize.x;
				}
				Point oldSize = tableCtl.getSize();
				if (oldSize.x > area.width) {
					// table is getting smaller so make the columns
					// smaller first and then resize the table to
					// match the client area width
					tc2.setWidth(width - tc1.getWidth());
					tableCtl.setSize(area.width, area.height);
				} else {
					// table is getting bigger so make the table
					// bigger first and then make the columns wider
					// to match the client area width
					tableCtl.setSize(area.width, area.height);
					tc2.setWidth(width - tc1.getWidth());
				}
			}
		});
	}

	/**
	 * add Listerner for send button
	 */
	private void addSendButtonListerner() {
		Listener listener = new Listener() {
			public void handleEvent(Event event) {
				if (event.widget == sendButton) {
					sendMessageAction.run();
				}
			}
		};
		sendButton.addListener(SWT.Selection, listener);

	}

	/**
	 * Create the table columns
	 */
	private void createTableColumns() {

		columnHeaders[PreferenceConstants.IMAGE_COLUMN] = "User";
		columnHeaders[PreferenceConstants.NAME_COLUMN] = "Message";

		tableCtl.setHeaderVisible(true);

		tc1 = new TableColumn(tableCtl, SWT.NONE);
		tc1.setText(columnHeaders[PreferenceConstants.IMAGE_COLUMN]);
		tc1.setAlignment(SWT.CENTER);
		tc1.setData(new Integer(PreferenceConstants.IMAGE_COLUMN));

		tc2 = new TableColumn(tableCtl, SWT.NONE);
		tc2.setText(columnHeaders[PreferenceConstants.NAME_COLUMN]);
		tc2.setAlignment(SWT.LEFT);
		tc2.setData(new Integer(PreferenceConstants.NAME_COLUMN));
	}

	/**
	 * Map the columns back to objects
	 * @return Object, representing the selected cell
	 */
	private TwitterStatus getSelectedStatus() {
		IStructuredSelection ss = (IStructuredSelection) tableViewer
				.getSelection();
		TwitterStatus[] ts = new TwitterStatus[ss.size()];
		System.arraycopy(ss.toArray(), 0, ts, 0, ts.length);

		return ts[0];
	}

	/**
	 * Add Doubleclicklistener, Content- and Labelprovider for the table with
	 * the needed Listener 
	 */
	private void addTableListeners() {
		tableViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				TwitterStatus status = getSelectedStatus();
				if (status != null)
					showMessage(status);
			}
		});

		tableViewer.setContentProvider(new SameTwitContentProvider());
		tableViewer.setLabelProvider(new SameTwitLabelProvider());

		Listener paintListener = new SameTwitTablePaintListener();
		tableCtl.addListener(SWT.PaintItem, paintListener);
		tableCtl.addListener(SWT.MeasureItem, paintListener);
		tableCtl.addListener(SWT.EraseItem, paintListener);

	}

	/**
	 * Call this function after changes in the representing data of the table
	 */
	private void refreshTable() {
		if (tableCtl == null || tableCtl.isDisposed())
			return;
		tableViewer.getControl().getDisplay().asyncExec(new Runnable() {
			public void run() {
				if (!tableCtl.isDisposed()) {
					// added to work around redraw problem of first column
					tableCtl.setRedraw(false);
					tableViewer.refresh();
					tableCtl.setRedraw(true);
				}
			}
		});
	}

	/**
	 * Callback
	 * Updates the view with a new status message
	 * 
	 * @param userStatus
	 */
	public void setUserStatus(final TwitterStatus userStatus) {
		try {
			twitterViewComp.getDisplay().asyncExec(new Runnable() {
				public void run() {
					twitterStatusLabel.setText(userStatus.getScreenName()
							+ ": " + userStatus.getMessage());
					twitterStatusLabel.setToolTipText(userStatus.getMessage());
					imageLabel.setImage(userStatus.getImage());
					// twitterViewComp.pack();
					// twitterViewComp.layout();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Callback: Call this after new data reception
	 * We refresh the table view
	 */
	public void handleTwitterRefresh() {
		refreshTable();
	}

	/**
	 * Method to provide very simple error message in the label of the
	 * usermessage
	 * 
	 * @param msg
	 */
	public void setErrorMessage(final String msg) {
		try {
			twitterViewComp.getDisplay().asyncExec(new Runnable() {
				public void run() {
					if (msg != null && twitterStatusLabel != null) {
						twitterStatusLabel.setText(msg);
						twitterStatusLabel.setToolTipText(msg);
					}
					errorMessage = msg;
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		tableViewer.getControl().setFocus();
	}

	/**
	 * Add refresh button to the menubar
	 */
	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		bars.getMenuManager().add(refreshAction);
		//bars.getMenuManager().add(new Separator());
	}

	/**
	 * Creates the Refresh and Send action
	 */
	private void makeActions() {
		refreshAction = new Action() {
			public void run() {
				TwitterData.getInstance().startTimer();
			}
		};
		refreshAction.setText("Refresh");
		refreshAction.setToolTipText("Refresh from Server");
		refreshAction.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages().getImageDescriptor(
						ISharedImages.IMG_OBJS_INFO_TSK));

		sendMessageAction = new Action() {
			public void run() {
				TwitterData.getInstance().sendStatus(newTwitterText.getText());
				newTwitterText.setText("");
				TwitterData.getInstance().startTimer();
			}
		};
		sendMessageAction.setText("Send");
		sendMessageAction.setToolTipText("Send Message");
		sendMessageAction.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages().getImageDescriptor(
						ISharedImages.IMG_OBJS_INFO_TSK));

	}

	/**
	 * Show a Twitter Message in a TwitterStatusDialog
	 * @param status
	 */
	private void showMessage(TwitterStatus status) {
		TwitterStatusDialog tss = new TwitterStatusDialog(tableViewer
				.getControl().getShell(), status.getImage());
		String message;
		message = status.getScreenName() + " wrote on "
				+ status.getMessageDate().toString() + "\n \n"
				+ status.getMessage();
		tss.setMessage(message);
		tss.open();

	}

	/**
	 * Returns a default image for calculation of the picture-size
	 * @return
	 */
	private Image getDefaultUserImage() {
		return Activator.getImageDescriptor("images/EmptyImage.gif")
				.createImage();
	}
}